---
title: "Python 代码质量、测试与类型提示"
description: "从 JavaScript 开发者视角学习 Python 的代码质量工具、单元测试和类型提示"
---

## 1. 引言

### 为什么代码质量、测试和类型提示很重要？

作为一名经验丰富的 JavaScript 开发者，你一定深知 `ESLint`、`Prettier`、`Jest` 和 `TypeScript` 在大型项目中的重要性。它们是确保代码一致性、可读性和可维护性的基石。

Python 生态系统同样拥有强大的工具集来解决这些问题。本模块将向你介绍 Python 中的代码质量、测试和类型提示工具，并与你熟悉的 JavaScript 工具进行对比。

**核心概念类比**

| Python 工具/概念 | JavaScript 工具/概念 | 主要功能 |
| --- | --- | --- |
| **Black** | `Prettier` | 自动化代码格式化 |
| **Ruff** | `ESLint` | 代码风格检查、错误检测 |
| **pytest** | `Jest` / `Mocha` | 单元测试、集成测试 |
| **Type Hints** | `TypeScript` | 静态类型检查 |
| **Mypy** | `tsc` (TypeScript Compiler) | 类型检查器 |

> 💡 **学习策略**：将 Python 的这些工具视为你现有 JavaScript 工作流的“Python 版本”。它们的目标和理念是相似的，只是具体实现和语法有所不同。

## 2. 代码质量与格式化

### 2.1 Black: 不妥协的代码格式化工具

`Black` 是 Python 社区广泛使用的代码格式化工具，它以“不妥协”而闻名，意味着它只提供极少的配置选项。这确保了在任何项目中，代码风格都能保持高度一致。

<PythonEditor title="代码格式化对比：Black vs. Prettier" compare={true} canRun={false} height={800}>
```javascript !! js
// JavaScript (使用 Prettier 格式化前)
function longFunctionName(arg1, arg2, arg3, arg4, arg5, arg6) {
  console.log('这是一个很长的函数定义，Prettier 会自动换行');
  if (arg1 && arg2 && arg3 && arg4 && arg5 && arg6) {
    return { key1: 'value1', key2: 'value2', key3: 'value3' };
  }
}

// Prettier 格式化后
function longFunctionName(
  arg1,
  arg2,
  arg3,
  arg4,
  arg5,
  arg6,
) {
  console.log(
    '这是一个很长的函数定义，Prettier 会自动换行',
  );
  if (
    arg1 &&
    arg2 &&
    arg3 &&
    arg4 &&
    arg5 &&
    arg6
  ) {
    return {
      key1: 'value1',
      key2: 'value2',
      key3: 'value3',
    };
  }
}
```

```python !! py
# Python (使用 Black 格式化前)
def long_function_name(arg1, arg2, arg3, arg4, arg5, arg6):
  print('这是一个很长的函数定义，Black 会自动换行')
  if arg1 and arg2 and arg3 and arg4 and arg5 and arg6:
    return {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}

# Black 格式化后
def long_function_name(
    arg1,
    arg2,
    arg3,
    arg4,
    arg5,
    arg6,
):
    print("这是一个很长的函数定义，Black 会自动换行")
    if (
        arg1
        and arg2
        and arg3
        and arg4
        and arg5
        and arg6
    ):
        return {
            "key1": "value1",
            "key2": "value2",
            "key3": "value3",
        }
```
</PythonEditor>

### 2.2 Ruff: 高性能的代码检查器

`Ruff` 是一个用 Rust 编写的 Python Linter，速度极快，可以替代 `Flake8`、`isort` 等多个工具。它能帮助你发现代码中的潜在问题、改进代码风格，并自动修复许多常见错误。

<PythonEditor title="代码检查对比：Ruff vs. ESLint" compare={true} canRun={false}>
```javascript !! js
// JavaScript (ESLint 可能会报告 'no-unused-vars')
import React from 'react'; // 'React' is defined but never used.

function MyComponent() {
  const unusedVar = 42; // 'unusedVar' is assigned a value but never used.
  return <div>Hello</div>;
}

// ESLint 还可以检查规则，如强制使用 ===
if (a == 1) { // '==' is not allowed, use '==='
  // ...
}
```

```python !! py
# Python (Ruff 可能会报告 'F841' 和 'F401')
import os  # 'os' imported but unused (F401)
import sys

def my_function():
    unused_var = 42  # local variable 'unused_var' is assigned to but never used (F841)
    print("Hello from my_function")

# Ruff 还可以检查许多其他问题，例如不必要的列表推导
# Instead of list(my_generator), use [...my_generator]
```
</PythonEditor>

## 3. 单元测试与 Pytest

`pytest` 是 Python 中最流行、功能最强大的测试框架。它以其简洁的语法、强大的断言功能和丰富的插件生态系统而著称。

### 3.1 基本测试用例

与 `Jest` 类似，`pytest` 允许你使用简单的函数来编写测试用例，而不需要复杂的类结构。

<PythonEditor title="基本测试对比：pytest vs. Jest" compare={true} canRun={false}>
```javascript !! js
// JavaScript (Jest)
// a_plus_b.js
const a_plus_b = (a, b) => a + b;

// a_plus_b.test.js
const a_plus_b = require('./a_plus_b');

describe('a_plus_b', () => {
  it('should return the sum of two numbers', () => {
    expect(a_plus_b(1, 2)).toBe(3);
  });

  it('should handle negative numbers', () => {
    expect(a_plus_b(-1, -1)).toBe(-2);
  });
});
```

```python !! py
# Python (pytest)
# a_plus_b.py
def a_plus_b(a, b):
    return a + b

# test_a_plus_b.py
from a_plus_b import a_plus_b

def test_a_plus_b():
    assert a_plus_b(1, 2) == 3

def test_a_plus_b_negative():
    assert a_plus_b(-1, -1) == -2
```
</PythonEditor>

### 3.2 参数化测试

`pytest` 的参数化测试功能非常强大，可以让你用不同的输入数据多次运行同一个测试函数，这在 `Jest` 中通常通过 `test.each` 实现。

<PythonEditor title="参数化测试对比：pytest vs. Jest" compare={true} canRun={false}>
```javascript !! js
// JavaScript (Jest)
// a_plus_b.test.js
const a_plus_b = require('./a_plus_b');

describe('a_plus_b', () => {
  test.each([
    [1, 2, 3],
    [-1, 1, 0],
    [0, 0, 0],
    [100, 200, 300],
  ])('a_plus_b(%i, %i) should return %i', (a, b, expected) => {
    expect(a_plus_b(a, b)).toBe(expected);
  });
});
```

```python !! py
# Python (pytest)
# test_a_plus_b.py
import pytest
from a_plus_b import a_plus_b

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (-1, 1, 0),
    (0, 0, 0),
    (100, 200, 300),
])
def test_a_plus_b_parameterized(a, b, expected):
    assert a_plus_b(a, b) == expected
```
</PythonEditor>

### 3.3 Fixtures: 管理测试依赖

`pytest` 的 `fixtures` 是一个核心特性，用于管理测试的依赖和状态。它们类似于 `Jest` 中的 `beforeEach` 和 `afterEach`，但更加灵活和强大。

<PythonEditor title="Fixtures 对比：pytest vs. Jest" compare={true} canRun={false}>
```javascript !! js
// JavaScript (Jest)
// db.js
class Database {
  constructor() { this.users = []; }
  connect() { /* ... */ }
  disconnect() { /* ... */ }
  addUser(user) { this.users.push(user); }
}

// db.test.js
const Database = require('./db');

describe('Database', () => {
  let db;

  beforeEach(() => {
    db = new Database();
    db.connect();
  });

  afterEach(() => {
    db.disconnect();
  });

  it('should add a user', () => {
    db.addUser({ name: 'Alice' });
    expect(db.users).toHaveLength(1);
  });
});
```

```python !! py
# Python (pytest)
# db.py
class Database:
    def __init__(self):
        self.users = []
    def connect(self): ...
    def disconnect(self): ...
    def add_user(self, user):
        self.users.append(user)

# test_db.py
import pytest

@pytest.fixture
def db():
    database = Database()
    database.connect()
    yield database
    database.disconnect()

def test_add_user(db):
    db.add_user({"name": "Alice"})
    assert len(db.users) == 1
```
</PythonEditor>

## 4. 类型提示与静态分析

Python 从 3.5 版本开始引入了类型提示（Type Hints），允许开发者为变量、函数参数和返回值添加类型信息。这与 `TypeScript` 的目标非常相似：在不改变运行时行为的情况下，提供静态类型检查，以提高代码的健壮性和可读性。

### 4.1 基本类型提示

<PythonEditor title="基本类型提示对比：Python vs. TypeScript" compare={true} canRun={false}>
```typescript !! ts
// TypeScript
function greet(name: string, age: number): string {
  return `Hello, ${name}. You are ${age} years old.`;
}

let user: { name: string; id: number } = { name: "Alice", id: 1 };
```

```python !! py
# Python
def greet(name: str, age: int) -> str:
    return f"Hello, {name}. You are {age} years old."

user: dict[str, int | str] = {"name": "Alice", "id": 1}
```
</PythonEditor>

### 4.2 复杂类型与泛型

和 `TypeScript` 一样，Python 的类型系统也支持更复杂的类型，如列表、字典、泛型和自定义类型。

<PythonEditor title="复杂类型对比：Python vs. TypeScript" compare={true} canRun={false}>
```typescript !! ts
// TypeScript
type User = {
  name: string;
  id: number;
};

function processUsers(users: User[]): number[] {
  return users.map(user => user.id);
}

function getFirst<T>(items: T[]): T | undefined {
  return items[0];
}
```

```python !! py
# Python
from typing import List, TypeVar, Optional, TypedDict

# 使用 TypedDict 更精确地定义字典结构
class User(TypedDict):
    name: str
    id: int

def process_users(users: List[User]) -> List[int]:
    return [user["id"] for user in users]

T = TypeVar('T')

def get_first(items: List[T]) -> Optional[T]:
    return items[0] if items else None
```
</PythonEditor>

### 4.3 Mypy: Python 的静态类型检查器

`Mypy` 是 Python 的静态类型检查器，它读取带类型提示的 Python 代码，分析并报告类型不一致的错误。它的作用类似于 `TypeScript` 的编译器 `tsc`。

<PythonEditor title="类型检查对比：Mypy vs. tsc" compare={true} canRun={false}>
```typescript !! ts
// TypeScript (tsc会报错)
function add(a: number, b: number): number {
  return a + b;
}

add(1, "2"); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
```

```python !! py
# Python (Mypy会报错)
def add(a: int, b: int) -> int:
    return a + b

add(1, "2")  # Error: Argument 2 to "add" has incompatible type "str"; expected "int"
```
</PythonEditor>

## 5. 总结

恭喜你！现在你已经了解了 Python 中保证代码质量、进行测试和实现静态类型检查的核心工具。

*   使用 **Black** 和 **Ruff** 来保持代码的整洁和一致。
*   使用 **pytest** 来编写清晰、可维护的测试用例。
*   使用 **Type Hints** 和 **Mypy** 来增强代码的健壮性和可读性。

将这些工具集成到你的开发工作流中，将大大提升你的 Python 开发体验，并帮助你写出更高质量的代码。
